package com.cicigodd.brick.plug.common.error;

import cn.hutool.core.util.StrUtil;
import com.cicigodd.brick.plug.common.exception.AuthException;
import com.cicigodd.brick.plug.common.exception.BusinessException;
import com.cicigodd.brick.plug.common.exception.ServiceException;
import com.cicigodd.brick.plug.common.response.Result;
import com.cicigodd.brick.plug.common.response.ResultGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import java.sql.SQLException;
import java.util.List;

/**
 * 全局异常处理器，拦截控制器层的异常
 *
 * @author cicigodd
 * @date 2022/09/23 08:42:00
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 请求参数缺失异常
     *
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public Result missingParam(MissingServletRequestParameterException missingServletRequestParameterException) {
        String parameterName = missingServletRequestParameterException.getParameterName();
        String parameterType = missingServletRequestParameterException.getParameterType();
        log.error("缺少请求参数,参数名：" + parameterName);
        return ResultGenerator.failResult("缺少请求参数,参数名：" + parameterName);
    }

//    /**
//     * HttpMessageConverter转化异常，一般为json解析异常
//     *
//     * @author cicigodd
//     * @date 2020/12/16 14:21
//     */
//    @ExceptionHandler(HttpMessageNotReadableException.class)
//    @ResponseBody
//    public Result httpMessageNotReadable(HttpMessageNotReadableException httpMessageNotReadableException) {
//        log.error("请求参数格式错误或字段类型错误，具体信息为：{}", httpMessageNotReadableException.getMessage());
//        return ResultGenerator.failResult("请求参数格式错误或字段类型错误");
//    }

    /**
     * 拦截不支持媒体类型异常
     *
     * @author cicigodd
     * @date 2020/12/16 14:26
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseBody
    public Result httpMediaTypeNotSupport(HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
        log.error("参数格式传递异常，具体信息为：{}", httpMediaTypeNotSupportedException.getMessage());
        return ResultGenerator.failResult("请求的http media type不合法");
    }

    /**
     * 不受支持的http method
     *
     * @author cicigodd
     * @date 2020/12/16 14:56
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public Result methodNotSupport(HttpServletRequest request) {
        String httpMethod = request.getMethod().toUpperCase();
        log.error("当前接口不支持" + httpMethod + "方式请求");
        return ResultGenerator.failResult("当前接口不支持" + httpMethod + "方式请求");
    }

    /**
     * 404找不到资源
     *
     * @author cicigodd
     * @date 2020/12/16 14:58
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseBody
    public Result notFound(NoHandlerFoundException e) {
        return ResultGenerator.failResult("404", "找不到请求的资源");
    }

    /**
     * SQL错误
     *
     * @author cicigodd
     * @date 2020/12/16 14:58
     */
    @ExceptionHandler(SQLException.class)
    @ResponseBody
    public Result sqlError(SQLException e) {
        return ResultGenerator.failResult("500", "SQL错误");
    }

    /**
     * 请求参数校验失败，拦截 @Valid 校验失败的情况
     *
     * @author cicigodd
     * @date 2020/12/16 14:59
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result methodArgumentNotValidException(MethodArgumentNotValidException e) {
        String bindingResult = getArgNotValidMessage(e.getBindingResult());
        log.error(bindingResult);
        return ResultGenerator.failResult(bindingResult);
    }

    /**
     * 请求参数校验失败，拦截 @Validated 校验失败的情况
     * <p>
     * 两个注解 @Valid 和 @Validated 区别是后者可以加分组校验，前者没有分组校验
     *
     * @author cicigodd
     * @date 2020/12/16 15:08
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public Result bindException(BindException e) {
        String bindingResult = getArgNotValidMessage(e.getBindingResult());
        log.error(bindingResult);
        return ResultGenerator.failResult(bindingResult);
    }


    /**
     * 拦截全校校验一类的异常
     * 如果用户登录过期，不是ajax请求，则直接跳转到登录页面，并提示会话超时
     *
     * @author cicigodd
     * @date 2020/12/16 15:11
     */
    @ExceptionHandler(AuthException.class)
    @ResponseBody
    public Result authError(AuthException e) {
        log.error(e.getMessage());
        return ResultGenerator.tokenFailResult(e.getMessage());
    }

    /**
     * 拦截业务代码抛出的异常
     *
     * @author cicigodd
     * @date 2020/12/16 15:11
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public Result businessError(BusinessException e) {
        log.error(e.getMessage());
        return ResultGenerator.failResult(e.getMessage());
    }

    /**
     * 拦截系统代码抛出的异常
     *
     * @author cicigodd
     * @date 2020/12/16 15:11
     */
    @ExceptionHandler(ServiceException.class)
    @ResponseBody
    public Result ServiceError(ServiceException e) {
        log.error(e.getMessage());
        return ResultGenerator.serverFailResult(e.getMessage());
    }


    /**
     * 拦截未知的运行时异常
     *
     * @author cicigodd
     * @date 2020/12/16 15:12
     */
    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public Result serverError(Throwable e) {
        log.error("服务器运行异常", e);
        return ResultGenerator.serverFailResult("服务器运行异常");
    }


    /**
     * 获取请求参数不正确的提示信息
     * 多个信息，拼接成用逗号分隔的形式
     *
     * @author cicigodd
     * @date 2020/5/5 16:50
     */
    private String getArgNotValidMessage(BindingResult bindingResult) {
        if (bindingResult == null) {
            return "";
        }
        StringBuilder stringBuilder = new StringBuilder();

        //多个错误用逗号分隔
        List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
        for (ObjectError error : allErrorInfos) {
            stringBuilder.append(",").append(error.getDefaultMessage());
        }

        //最终把首部的逗号去掉
        return StrUtil.removePrefix(stringBuilder.toString(), ",");
    }


}
